package at.technikum_wien.embsys.aat.PriscillaCore.component;

import java.util.List;

import at.technikum_wien.embsys.aat.PriscillaCore.enums.DayNightState;
import at.technikum_wien.embsys.aat.PriscillaCore.enums.EnoceanComponentType;
import at.technikum_wien.embsys.aat.PriscillaCore.enums.RainState;
import at.technikum_wien.embsys.aat.PriscillaCore.telegram.Telegram;



/**
 * 
 * EEP_07_13_01 component class - environmental applications
 *
 * Type: environmental applications - weather station
 * Dawn sensor
 * Temperature: -40 - 80°C
 * Wind speed 0-70m/s
 * Day/night sensor
 * Rain sensor
 * 
 * @author B. Aigner
 * 
 */
public class EEP_07_13_01 implements EnoceanComponent {
	
	Telegram telegram;
	List<?> eventListener;
	
	float illumination = -300;
	float temperature = -300;
	float windSpeed = -300;
	int identifier;
	DayNightState dayNight;
	RainState rain;
	
	EnoceanComponentType enoceanComponentType = EnoceanComponentType.EEP_07_13_01;
	boolean telegramCreation = false;
	
	int illumination_temp;
	float illumination_max = 999;
	float illumination_min = 0;
	int illumination_value_max = 255;
	int illumination_value_min = 0;
	
	int temperature_temp;
	float temperature_max = 80;
	float temperature_min = -40;
	int temperature_value_max = 255;
	int temperature_value_min = 0;
	
	int windSpeed_temp;
	float windSpeed_max = 70;
	float windSpeed_min = 0;
	int windSpeed_value_max = 255;
	int windSpeed_value_min = 0;
	
	float multiplier;

	/**
	 * This constructor is used for received packets.
	 * It will parse the telegram and set the corresponding members.
	 * 
	 * @param telegram Received Telegram
	 * @param listener Event subscriber list
	 */
	public EEP_07_13_01(Telegram telegram, List<?> listener) {
		super();
		this.telegram = telegram;
		this.eventListener = listener;
		
		illumination_temp = telegram.getData3();
		temperature_temp = telegram.getData2();
		windSpeed_temp = telegram.getData1();

		identifier = (telegram.getData0() & 0xF0) / 16;

		if(checkBit(telegram.getData0(), 2)) {
			dayNight = DayNightState.NIGHT;
		} else {
			dayNight = DayNightState.DAY;
		}
		
		if(checkBit(telegram.getData0(), 1)) {
			rain = RainState.RAIN;
		} else {
			rain = RainState.NO_RAIN;
		}

		multiplier = (illumination_max - illumination_min) / (illumination_value_max - illumination_value_min);

		if(multiplier >= 0) {
			this.illumination = multiplier * illumination_temp + illumination_max;
		} else {
			this.illumination = multiplier * illumination_temp + illumination_min;
		}


		multiplier = (temperature_max - temperature_min) / (temperature_value_max - temperature_value_min);

		if(multiplier >= 0) {
			this.temperature = multiplier * temperature_temp + temperature_max;
		} else {
			this.temperature = multiplier * temperature_temp + temperature_min;
		}


		multiplier = (windSpeed_max - windSpeed_min) / (windSpeed_value_max - windSpeed_value_min);

		if(multiplier >= 0) {
			this.windSpeed = multiplier * windSpeed_temp + windSpeed_max;
		} else {
			this.windSpeed = multiplier * windSpeed_temp + windSpeed_min;
		}
		fireFrame();
	}

	/**
	 * Fire all associated events
	 */
	void fireFrame() {
		//TODO: WatherStation Event
		/*Iterator listen = eventListener.iterator();
		final FrameEventFan frameEventFan = new FrameEventFan(this, telegram.getDeviceID(), this.fanspeed, this.fantype );
		while (listen.hasNext()) {
			((LinkListener) listen.next()).frameReceived_Fan(frameEventFan);
		}*/
	}

	/**
	 * Void constructor. It is used to create a new telegram for sending
	 */
	public EEP_07_13_01() {
		super();
		telegram = new Telegram();
		telegramCreation = true;
	}

	/**
	 * This constructor is used to create a telegram (the device id is given as parameter)
	 * @param deviceID Full device ID
	 */
	public EEP_07_13_01(String deviceID) {
		super();
		telegram = new Telegram();
		telegram.setDeviceID(deviceID);
		telegramCreation = true;
	}
	
	boolean checkBit(int value, int bitNr) {
		return (value & (1 << bitNr)) != 0;
	}
	
	int setBit(int value, int bitNr) {
		return value | (1<<bitNr);
	}
	
	int clrBit(int value, int bitNr) {
		return value & (~(1<<bitNr));
	}
	
	@Override
	public String getDeviceID() {
		return telegram.getDeviceID();
	}
	@Override
	public String getORG() {
		return telegram.getxORG();
	}
	@Override
	public EnoceanComponentType getType() {
		return enoceanComponentType;
	}

	@Override
	public void setDeviceID(String deviceID) {
		telegram.setDeviceID(deviceID);
	}

	/**
	 * Creates a new telegram from the component
	 * 
	 * This method creates a new telegram (if the component is initialized with
	 * a constructor without a telegram as parameter) or returns the saved telegram.
	 * The new telegram is built up from the members in this class (in this case environmental data). 
	 * @return Created telegram
	 */
	public Telegram getTelegram() {
		if(telegramCreation == true) {

			multiplier = (illumination_max - illumination_min) / (illumination_value_max - illumination_value_min);
			if(multiplier < 0) {
				illumination_temp = (int)((this.illumination - illumination_max) / multiplier);
			} else {
				illumination_temp = (int)((this.illumination - illumination_min) / multiplier);
			}
			telegram.setData3(illumination_temp);
			
			multiplier = (temperature_max - temperature_min) / (temperature_value_max - temperature_value_min);
			if(multiplier < 0) {
				temperature_temp = (int)((this.temperature - temperature_max) / multiplier);
			} else {
				temperature_temp = (int)((this.temperature - temperature_min) / multiplier);
			}
			telegram.setData2(temperature_temp);
			
			multiplier = (windSpeed_max - windSpeed_min) / (windSpeed_value_max - windSpeed_value_min);
			if(multiplier < 0) {
				windSpeed_temp = (int)((this.windSpeed - windSpeed_max) / multiplier);
			} else {
				windSpeed_temp = (int)((this.windSpeed - windSpeed_min) / multiplier);
			}
			telegram.setData2(windSpeed_temp);
			
			telegram.setData0(identifier*16);

			if(dayNight == DayNightState.DAY) {
				telegram.setData0(clrBit(telegram.getData0(),2));
			} else {
				telegram.setData0(setBit(telegram.getData0(),2));
			}
			
			if(rain == RainState.NO_RAIN) {
				telegram.setData0(clrBit(telegram.getData0(),1));
			} else {
				telegram.setData0(setBit(telegram.getData0(),1));
			}
				
		}
		return telegram;
	}

	public float getIllumination() {
		return illumination;
	}

	public float getTemperature() {
		return temperature;
	}

	public float getWindSpeed() {
		return windSpeed;
	}

	public int getIdentifier() {
		return identifier;
	}

	public DayNightState getDayNight() {
		return dayNight;
	}

	public RainState getRain() {
		return rain;
	}
	
	public void setIllumination(float illumination) {
		if(illumination > illumination_max) return;
		if(illumination < illumination_min) return;
		this.illumination = illumination;
	}
	
	public void setTemperature(float temperature) {
		if(temperature > temperature_max) return;
		if(temperature < temperature_min) return;
		this.temperature = temperature;
	}
	
	public void setWindSpeed(float windSpeed) {
		if(windSpeed > windSpeed_max) return;
		if(windSpeed < windSpeed_min) return;
		this.windSpeed = windSpeed;
	}

	public void setIdentifier(int identifier) {
		if(identifier > 15) return;
		this.identifier = identifier;
	}

	public void setDayNight(DayNightState dayNight) {
		this.dayNight = dayNight;
	}

	public void setRain(RainState rain) {
		this.rain = rain;
	}
}
